package com.sh.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sh.common.cache.DictCache;
import com.sh.common.enums.BizCodeEnum;
import com.sh.common.enums.UsedEnum;
import com.sh.common.pojo.bo.OperatorFile;
import com.sh.common.pojo.bo.OperatorFuncParam;
import com.sh.common.pojo.bo.OperatorResult;
import com.sh.common.pojo.dto.OperatorDto;
import com.sh.common.pojo.dto.OperatorParseResultDto;
import com.sh.common.pojo.query.OperatorPageQuery;
import com.sh.config.CalcConfig;
import com.sh.entity.Operator;
import com.sh.entity.OperatorParam;
import com.sh.exceptions.BizException;
import com.sh.mapper.OperatorMapper;
import com.sh.service.IOperatorService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * 算子表 服务实现类
 * </p>
 *
 * @author zhenlan
 * @since 2024-07-10
 */
@Slf4j
@Service
public class OperatorServiceImpl extends ServiceImpl<OperatorMapper, Operator> implements IOperatorService {

    @Autowired
    private CalcConfig calcConfig;

    @Autowired
    private OperatorMapper operatorMapper;

    @Override
    public IPage<OperatorDto> queryOperatorPage(OperatorPageQuery query) {
        Page<Operator> page = new Page<>(query.getPageNum(), query.getPageSize());
        page.addOrder(OrderItem.desc("id"));
        QueryWrapper<Operator> queryWrapper = new QueryWrapper<>();
        if (StringUtils.isNotBlank(query.getName())) {
            queryWrapper.like("name", query.getName());
        }
        if (StringUtils.isNotBlank(query.getNameEn())) {
            queryWrapper.like("name_en", query.getNameEn());
        }
        if (StringUtils.isNotBlank(query.getSubjectCode())) {
            queryWrapper.eq("subject_code", query.getSubjectCode());
        }
        if (StringUtils.isNotBlank(query.getApplicableTypeCode())) {
            queryWrapper.eq("applicable_type_code", query.getApplicableTypeCode());
        }
        if (query.getUsed() != null) {
            queryWrapper.eq("used", query.getUsed());
        }
        IPage<Operator> result = operatorMapper.selectPage(page, queryWrapper);
        return result.convert(this::transferToDto);
    }

    @Override
    public OperatorDto transferToDto(Operator operator) {
        if (operator == null) {
            return null;
        }
        OperatorDto operatorDto = new OperatorDto();
        BeanUtil.copyProperties(operator, operatorDto);
        operatorDto.setSubjectName(DictCache.OPERATOR_SUBJECT_MAP.get(operator.getSubjectCode()));
        operatorDto.setApplicableTypeName(DictCache.OPERATOR_APPLICABLE_TYPE_MAP.get(operator.getApplicableTypeCode()));
        operatorDto.setUsedName(UsedEnum.getMessageByCode(operator.getUsed()));
        if (StringUtils.isNotBlank(operator.getOperatorFileDetail())) {
            operatorDto.setOperatorFiles(JSON.parseArray(operator.getOperatorFileDetail(), OperatorFile.class));
        }
        if (StringUtils.isNotBlank(operator.getResultInfo())) {
            operatorDto.setOperatorResults(JSON.parseArray(operator.getResultInfo(), OperatorResult.class));
        }
        if (StringUtils.isNotBlank(operator.getFuncParamInfo())) {
            operatorDto.setOperatorFuncParams(JSON.parseArray(operator.getFuncParamInfo(), OperatorFuncParam.class));
        }
        if (StringUtils.isNotBlank(operator.getParamInfo())) {
            operatorDto.setOperatorParams(JSON.parseArray(operator.getParamInfo(), OperatorParam.class));
        }
        return operatorDto;
    }

    @Override
    public List<OperatorDto> queryOperatorTree(OperatorPageQuery query) {
        List<Operator> operatorListTree = operatorMapper.getOperatorListTree(query);
        List<OperatorDto> ret = new ArrayList<>();
        operatorListTree.forEach(operator -> {
            OperatorDto operatorDto = transferToDto(operator);
            ret.add(operatorDto);
        });
        return ret;
    }

    @Override
    public OperatorParseResultDto parseOperator(String filePath, String entryFile) throws BizException {
        HashMap<String, Object> map = new HashMap<>();
        map.put("operator_url", filePath);
        map.put("operator_file_name", entryFile);
        log.info("parse operator file, url {}, params: {}", calcConfig.getOperatorParseUrl(), map);
        String rep = HttpUtil.get(calcConfig.getOperatorParseUrl(), map);
        log.info("parse operator file result, {}", rep);
        JSONObject jsonObject = JSON.parseObject(rep);
        if (!jsonObject.containsKey("success") || !jsonObject.getBoolean("success")) {
            log.warn("parse operator file error!!!");
            throw new BizException(BizCodeEnum.OPERATOR_PARSE_ERROR.getCode(), jsonObject.getString("message"));
        }
        JSONObject data = jsonObject.getJSONObject("data");
        JSONObject params = data.getJSONObject("params");
        List<OperatorParam> operatorParams = new ArrayList<>();
        for (Map.Entry<String, Object> entry : params.entrySet()) {
            OperatorParam operatorParam = new OperatorParam();
            JSONObject paramObject = (JSONObject) entry.getValue();
            operatorParam.setName(paramObject.getString("name"));
            operatorParam.setValueType(paramObject.getString("type"));
            operatorParam.setDefaultValue(paramObject.getString("default_val"));
            operatorParam.setRequired("required".equals(paramObject.getString("optional_field")) ? 1 : 0);
            operatorParams.add(operatorParam);
        }
        JSONObject results = data.getJSONObject("result");
        ArrayList<OperatorResult> operatorResults = new ArrayList<>();
        for (Map.Entry<String, Object> entry : results.entrySet()) {
            OperatorResult operatorResult = new OperatorResult();
            JSONObject resultObject = (JSONObject) entry.getValue();
            operatorResult.setName(resultObject.getString("name"));
            operatorResult.setValueType(resultObject.getString("type"));
            operatorResults.add(operatorResult);
        }

        return OperatorParseResultDto.builder()
                .operatorParams(operatorParams)
                .operatorResults(operatorResults)
                .build();
    }
}
